package top.ddltech.stubborn.graphics3D;

import top.ddltech.stubborn.graphics3D.texture.ShadedTexture;
import top.ddltech.stubborn.graphics3D.texture.Texture;
import top.ddltech.stubborn.math3D.*;

import java.awt.*;

/**
 * The ShadedTexturedPolygonRenderer class is a PolygonRenderer
 * that renders ShadedTextured dynamically with one light source.
 * By default, the ambient light intensity is 0.5 and there
 * is no point light.
 */
public class ShadedTexturedPolygonRenderer
        extends FastTexturedPolygonRenderer {

    private PointLight3D lightSource;
    private float ambientLightIntensity = 0.5f;
    private Vector3D directionToLight = new Vector3D();

    public ShadedTexturedPolygonRenderer(Transform3D camera,
                                         ViewWindow viewWindow) {
        this(camera, viewWindow, true);
    }

    public ShadedTexturedPolygonRenderer(Transform3D camera,
                                         ViewWindow viewWindow, boolean clearViewEveryFrame) {
        super(camera, viewWindow, clearViewEveryFrame);
    }

    /**
     * Gets the light source for this renderer.
     */
    public PointLight3D getLightSource() {
        return lightSource;
    }

    /**
     * Sets the light source for this renderer.
     */
    public void setLightSource(PointLight3D lightSource) {
        this.lightSource = lightSource;
    }

    /**
     * Gets the ambient light intensity.
     */
    public float getAmbientLightIntensity() {
        return ambientLightIntensity;
    }

    /**
     * Sets the ambient light intensity, generally between 0 and
     * 1.
     */
    public void setAmbientLightIntensity(float i) {
        ambientLightIntensity = i;
    }

    @Override
    protected void drawCurrentPolygon(Graphics2D g) {
        // set the shade level of the polygon before drawing it
        if (sourcePolygon instanceof TexturedPolygon3D) {
            TexturedPolygon3D poly =
                    ((TexturedPolygon3D) sourcePolygon);
            Texture texture = poly.getTexture();
            if (texture instanceof ShadedTexture) {
                calcShadeLevel();
            }
        }
        super.drawCurrentPolygon(g);
    }

    private void calcShadeLevel() {
        TexturedPolygon3D poly = (TexturedPolygon3D) sourcePolygon;
        float intensity = 0;
        if (lightSource != null) {
            // average all the vertices in the polygon
            directionToLight.setTo(0, 0, 0);
            for (int i = 0; i < poly.getNumVertices(); i++) {
                directionToLight.add(poly.getVertex(i));
            }
            directionToLight.divide(poly.getNumVertices());

            // make the vector from the average vertex
            // to the light
            directionToLight.subtract(lightSource);
            directionToLight.multiply(-1);

            // get the distance to the light for falloff
            float distance = directionToLight.length();

            // compute the diffuse reflect
            directionToLight.normalize();
            Vector3D normal = poly.getNormal();
            intensity = lightSource.getIntensity(distance)
                    * directionToLight.getDotProduct(normal);
            intensity = Math.min(intensity, 1);
            intensity = Math.max(intensity, 0);
            int level =
                    Math.round(intensity * ShadedTexture.MAX_LEVEL);
            ((ShadedTexture) poly.getTexture()).
                    setDefaultShadeLevel(level);
        }
    }
}
